import platform
import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from PIL import Image
import os
import matplotlib.font_manager as fm

# 切换为TkAgg后端（支持交互显示）
plt.switch_backend('TkAgg')
# ----------------------
# 字符集配置 - 解决字符显示不全问题
# ----------------------
def configure_fonts():
    """配置matplotlib字体，确保中文等字符正常显示"""
    # 尝试多种常见中文字体，确保至少有一种可用
    # 根据操作系统添加可能的字体
    system = platform.system()
    font_families = ["STKAITI"]
    # 查找系统中可用的字体
    available_fonts = [f for f in font_families if any(f in font for font in fm.findSystemFonts())]
    if available_fonts:
        plt.rcParams["font.family"] = available_fonts
        print(f"已配置字体: {available_fonts[0]}")
    else:
        print("警告: 未找到中文字体，可能导致显示异常")
        # 使用默认字体并启用unicode支持
        plt.rcParams["font.family"] = "sans-serif"
        plt.rcParams["axes.unicode_minus"] = False  # 解决负号显示问题


# 加载图像并转换为灰度图
def load_images_from_folder(folder, max_images=100, img_size=(100, 100)):
    images = []
    for filename in os.listdir(folder):
        if len(images) >= max_images:
            break
        try:
            img = Image.open(os.path.join(folder, filename)).convert('L')  # 转换为灰度图
            img = img.resize(img_size)  # 调整图像大小为统一尺寸
            img_array = np.array(img).flatten()  # 展平为一维向量
            images.append(img_array)
        except Exception as e:
            print(f"无法加载图像 {filename}: {e}")
    return np.array(images)


# 主函数
def main():
    # 配置字体
    configure_fonts()

    # 假设图像存储在"images"文件夹中
    image_folder = "images"
    images = load_images_from_folder(image_folder)

    # 检查数据形状并输出信息
    print(f"加载了 {images.shape[0]} 张图像")
    if images.shape[0] == 0:
        print("没有加载到任何图像，请检查文件夹路径和图像文件")
        return

    print(f"每张图像的维度为 {images.shape[1]}")

    # 计算最大可能的主成分数量
    max_possible_components = min(images.shape[0], images.shape[1])
    print(f"最大可能的主成分数量: {max_possible_components}")

    # 数据标准化
    scaler = StandardScaler()
    images_scaled = scaler.fit_transform(images)

    # 应用PCA
    def apply_pca(images, n_components):
        pca = PCA(n_components=n_components)
        images_pca = pca.fit_transform(images)

        # 计算解释方差比
        explained_variance = np.sum(pca.explained_variance_ratio_)

        # 重建图像
        images_reconstructed = pca.inverse_transform(images_pca)

        return images_pca, images_reconstructed, explained_variance, pca

    # 根据最大可能的主成分数量调整我们要尝试的数量
    max_components_to_try = min(max_possible_components, 200)
    step = max(1, max_components_to_try // 5)  # 确保至少有1的步长
    components_list = list(range(1, max_components_to_try + 1, step))

    # 如果没有足够的组件，使用最大可能值
    if not components_list or max_components_to_try < 1:
        components_list = [1] if max_possible_components >= 1 else []

    print(f"将尝试的主成分数量: {components_list}")

    results = {}

    for n_components in components_list:
        print(f"应用 {n_components} 个主成分...")
        pca_result, recon_result, var_ratio, pca = apply_pca(images_scaled, n_components)
        results[n_components] = {
            'pca_result': pca_result,
            'reconstructed': recon_result,
            'variance_ratio': var_ratio,
            'pca': pca
        }
        print(f"保留的方差比例: {var_ratio:.4f}")

    # 可视化原始图像和重建图像
    def plot_comparison(original, reconstructed, n_components, variance_ratio, index=0):
        plt.figure(figsize=(10, 5))

        # 原始图像
        plt.subplot(1, 2, 1)
        plt.imshow(original[index].reshape(100, 100), cmap='gray')
        plt.title('原始图像')
        plt.axis('off')

        # 重建图像
        plt.subplot(1, 2, 2)
        plt.imshow(reconstructed[index].reshape(100, 100), cmap='gray')
        plt.title(f'使用 {n_components} 个主成分重建\n保留方差: {variance_ratio:.2%}')
        plt.axis('off')

        plt.tight_layout()
        plt.show()

    # 可视化第一个图像的重建结果
    for n_components in components_list:
        plot_comparison(images_scaled, results[n_components]['reconstructed'],
                        n_components, results[n_components]['variance_ratio'])

    # 绘制解释方差比例曲线
    pca_full = PCA().fit(images_scaled)
    plt.figure(figsize=(10, 6))
    plt.plot(np.cumsum(pca_full.explained_variance_ratio_))
    plt.xlabel('主成分数量')
    plt.ylabel('累积解释方差比例')
    plt.title('主成分数量与累积解释方差比例关系')
    plt.grid(True)

    # 标记我们尝试的主成分数量
    for n in components_list:
        if n - 1 < len(pca_full.explained_variance_ratio_):
            var = np.cumsum(pca_full.explained_variance_ratio_)[n - 1]
            plt.axvline(x=n, color='r', linestyle='--', alpha=0.5)
            plt.axhline(y=var, color='g', linestyle='--', alpha=0.5)
            plt.scatter(n, var, color='red')
            plt.text(n + 5, var - 0.05, f'{var:.2%}', color='red')

    plt.show()


if __name__ == "__main__":
    main()
